package za.org.jcicada.comms.impl;

import java.lang.reflect.Method;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import za.org.jcicada.comms.messages.CommsMessage;

/**
 * Provides a simple message handler that introspects the received message class
 * and calls an appropriate method on the consumer provided. The name of the
 * method is constructed from the canonical name of the message class by
 * combining the third last and last parts. Reflection is then used to locate a
 * method with the correct name and which takes one argument, namely an instance
 * of the message class.
 * 
 * For example, if the message class was fuzz.foo.bar.Baz, the method called
 * would be fooBaz (fuzz.foo.bar.Baz message).
 * 
 * If the message cannot be processed or any reason, an error is logged but no
 * exception is returned.
 */
public class JavaMessageHandler {
    /** Logger handle */
    private final Log logger = LogFactory.getLog(JavaMessageHandler.class);

    /** The object providing process methods for messages hanled by this handler */
    private Object consumer;

    /**
     * Constructs the handler. Simply sets the consumer object to the object
     * provided.
     * 
     * @param consumer the consumer that provides process methods
     */
    public JavaMessageHandler(Object consumer) {
        this.consumer = consumer;
    }

    /**
     * Handles a received message.
     * 
     * @param message the message to handle
     * @throws za.org.jcicada.comms.exceptions.CommsException if the consumer does
     *             not provide a process method for a message
     */
	public void process(CommsMessage message) {
        String methodName = null;

        try {
            String[] composition = message.getClass().getCanonicalName().split("\\.");
            methodName = composition[composition.length - 3] + composition[composition.length - 1];
            @SuppressWarnings("rawtypes")
			Class[] params = { message.getClass() };
            Method m = null;
            m = consumer.getClass().getMethod(methodName, params);
            if (logger.isTraceEnabled()) {
            	logger.trace(String.format("Dispatching call to message handler: %s", methodName));
            }
            m.invoke(consumer, message);
        } catch (Exception ex) {
            if (methodName == null) {
                logger.error("Error determining name of method to call.", ex);
            } else {
                logger.error("Error calling handler method " + methodName + ".", ex);
            }
        }
    }
}
